(function($){
	(function(){
		var DynamicHeader = function(header){
			var element = $(header).find('.dynamic-header').get(0);
			if(element == undefined) return element;
			var defaultContent = element.innerHTML;
			var content = defaultContent;

			var self = this;

			this.set = function(newContent){
				content = newContent;
				element.innerHTML = newContent;
			}
			this.restore = function(){
				content = defaultContent;
				element.innerHTML = content;
			}
		}
		DynamicHeader.prototype.setFromPanel = function(panel){
			var customHeader = panel.find('header').get(0)?
								panel.find('header').get(0):
								$('#'+panel.data('header')).get(0);
							
			if(customHeader){
				this.set(customHeader.innerHTML);
			}
			else {
				this.restore();
			}
		}
		var UI = function(){
			var element;
			var header;
			var dynamicHeader;
			var backButton;
			var content;
			var menu;
			var panels;
			var mask;
			var splashScreen;
			var shownPanel;
			var history;
			var transitionQueue;


			var uiReadyCallback = function(){};

			var _calculateContentHeight = function(){
				var headerHeight = header.offsetHeight;
				content.style.height = element.height() - headerHeight + 'px';
			}
			var _executeFunctionByName = function(functionName, context /*, args */) {
				var args = Array.prototype.slice.call(arguments, 2);
				var namespaces = functionName.split(".");
				var func = namespaces.pop();
				for (var i = 0; i < namespaces.length; i++) {
					context = context[namespaces[i]];
				}
				return context[func].apply(context, args);
			}

			var TransitionQueue = function(){
				var QueueEntry = function(fromId,toId,transition,back,ignoreHistory){
					return {
						toPanel : $(toId)[0],
						fromPanel : $(fromId)[0],
						back : back,
						run: function(){
							if(!$(this.toPanel).hasClass('panel') || this.toPanel == shownPanel){
								console.log('Panel already shown!');
								queue.shift();
								if(queue.length != 0)
									queue[0].run();
								return;
							}
							if(!ignoreHistory){
								//Log the transition in history
								if(history.isEmpty())
									$.ui.showBackButton();
								history.push(fromId,toId,transition);
							}

							var transitionFunc = $.ui.transitions[transition];

							if(transitionFunc && typeof transitionFunc=='function')
								transitionFunc(this.fromPanel,this.toPanel,this.back);
							else
								$.ui.transitions['default'](this.fromPanel,this.toPanel,this.back);
						}
					}
				}
				var queue = [];

				return {
					push : function(fromId,toId,transition,back,ignoreHistory){
						// console.log('pushing: '+fromId +', ' + toId +', ' + back + ', ' + ignoreHistory);
						queue.push(new QueueEntry(fromId,toId,transition,back,ignoreHistory));
						if(queue.length == 1)
							queue[0].run();
					},
					pop : function(){
						return queue.pop();
					},
					shift : function(){
						return queue.shift();
					},
					clear : function(){
						queue = [];
					},
					isEmpty : function(){
						return queue.length==0;
					},
					next : function(){
						queue.shift();
						if(queue.length != 0){
							console.log('calling run');
							queue[0].run();
						}
					}

				}
			}

			function _initialize(){
				history = new History();
				transitionQueue = new TransitionQueue();

				element = $('#UI');

				header = $('#UI>.header').get(0)||{};

				dynamicHeader = new DynamicHeader(header);

				backButton = $('#backButton')||{};
				backButton.bind('click',function(){
					$.ui.goBack();
				});
				this.hideBackButton();

				content = $('#UI>.content').get(0)||{};

				menu = $('#UI>.menu').get(0)||{};
				menu.shown = false;

				splashScreen = $('#UI>.splashScreen').get(0);
				if(splashScreen)
					splashScreen.shown = true;

				$('#UI .menu-link').bind('click',function(e){
					e.preventDefault();
				});

				panels = $('#UI>.content>.panel')||{};

				mask = $('<div class="mask" />');
				mask.appendTo(element);
				_calculateContentHeight();

				//Get all the shown panels
				var shownPanels = $('#UI>.content').find('.panel.shown');

				//If no panel had the 'shown' class, add it to the first panel
				//else remove the 'shown' class from all the shown panels except the last one
				if(shownPanels.length == 0){
					shownPanel = panels[0];
					$(panels[0]).addClass('shown');
				}
				else {
					for(var i=0;i<shownPanels.length-1;i++){
						$(shownPanels[i]).removeClass('shown');
					}
					shownPanel = shownPanels[shownPanels.length-1];
				}
				shownPanel.style.display = 'block';
				$(shownPanel).css3Animate({
					x: '0%',
					y: '0%'
				});

				var onLoadFn = window[$(shownPanel).data('onLoad')];
				if(typeof onLoadFn == 'function')
					onLoadFn();

				dynamicHeader.setFromPanel($(shownPanel));

			};

			/**
				Transitions
			*/
			var Transitions = {
				onTransitionEnd : function(oldDiv,newDiv){
					// $(oldDiv).css3Animate({
					// 	x: '100%'
					// });

					$.ui.finishTransition(oldDiv,newDiv);

					shownPanel = newDiv;
					transitionQueue.next();
				},
				slidePanel : function(oldDiv,newDiv,back){
					if(!oldDiv || !newDiv || (oldDiv == newDiv))
						return;

					oldDiv.style.display = "block";
					newDiv.style.display = "block";
									
					$.ui.block();

					dynamicHeader.setFromPanel($(newDiv));

					var onUnloadFn = $(oldDiv).data('onUnload');
					if(onUnloadFn)
						_executeFunctionByName(onUnloadFn,window);
											
					$.ui.unblock();

					var onLoadFn = $(newDiv).data('onLoad');
					if(onLoadFn)
						_executeFunctionByName(onLoadFn,window);

					if(back){
						$(oldDiv).css3Animate({
							x:'0%',
							y:'0%',
							complete: function(){
								$(oldDiv).css3Animate({
									x:'100%',
									time: $.ui.transitionTime,
									complete : function(){
										Transitions.onTransitionEnd(oldDiv,newDiv);
									}
								}).link(newDiv,{
									x:'0%',
									time: $.ui.transitionTime
								});
							}
						}).link(newDiv,{
							x:'-100%',
							y:'0%'
						});
					}
					else{
						$(oldDiv).css3Animate({
							x:'0%',
							y:'0%',
							complete: function(){
								$(oldDiv).css3Animate({
									x:'-100%',
									time: $.ui.transitionTime,
									complete : function(){
										Transitions.onTransitionEnd(oldDiv,newDiv);
									}
								}).link(newDiv,{
									x:'0%',
									time: $.ui.transitionTime
								});
							}
						}).link(newDiv,{
							x:'100%',
							y:'0%'
						});
					}
				},
				fadePanel : function(oldDiv,newDiv,back){
					if(!oldDiv || !newDiv || (oldDiv == newDiv))
						return;

					oldDiv.style.display = "block";
					newDiv.style.display = "block";
									
					$.ui.block();

					dynamicHeader.setFromPanel($(newDiv));

					var onUnloadFn = $(oldDiv).data('onUnload');
					if(onUnloadFn)
						_executeFunctionByName(onUnloadFn,window);
											
					$.ui.unblock();

					var onLoadFn = $(newDiv).data('onLoad');
					if(onLoadFn)
						_executeFunctionByName(onLoadFn,window);

					var self = this;
					if(back){
						newDiv.style.zIndex = 1;
						oldDiv.style.zIndex = 2;
						$(oldDiv).css3Animate({
							x:'0%',
							y:'0%',
							scale: 1,
							origin: "50%" + " 50%",
							opacity: 1,
							complete: function(){
								$(oldDiv).css3Animate({
									opacity: 0,
									scale: 0.9,
									origin: "50%" + " 50%",
									time: $.ui.transitionTime,
									complete : function(){
										Transitions.onTransitionEnd(oldDiv,newDiv);
									}
								}).link(newDiv,{
									time: $.ui.transitionTime
								});
							}
						}).link(newDiv,{
							x:'0%',
							y:'0%',
							opacity: 1
						});
					}
					else{
						oldDiv.style.zIndex = 1;
						newDiv.style.zIndex = 2;
						$(oldDiv).css3Animate({
							x:'0%',
							y:'0%',
							opacity: 1,
							complete: function(){
								$(oldDiv).css3Animate({
									time: $.ui.transitionTime,
									complete : function(){
										Transitions.onTransitionEnd(oldDiv,newDiv);
									}
								}).link(newDiv,{
									opacity: 1,
									scale: 1,
									origin: "50%" + " 50%",
									time: $.ui.transitionTime
								});
							}
						}).link(newDiv,{
							x:'0%',
							y:'0%',
							opacity: 0,
							scale: 0.9,
							origin: "50%" + " 50%",
						});
					}
				},
				slideUpPanel : function(oldDiv,newDiv,back){
					if(!oldDiv || !newDiv || (oldDiv == newDiv))
						return;

					oldDiv.style.display = "block";
					newDiv.style.display = "block";
									
					$.ui.block();

					dynamicHeader.setFromPanel($(newDiv));

					var onUnloadFn = $(oldDiv).data('onUnload');
					if(onUnloadFn)
						_executeFunctionByName(onUnloadFn,window);
											
					$.ui.unblock();

					var onLoadFn = $(newDiv).data('onLoad');
					if(onLoadFn)
						_executeFunctionByName(onLoadFn,window);

					if(back){
						$(oldDiv).css3Animate({
							x:'0%',
							y:'0%',
							complete: function(){
								$(oldDiv).css3Animate({
									y: '100%',
									time: $.ui.transitionTime,
									complete : function(){
										Transitions.onTransitionEnd(oldDiv,newDiv);
									}
								}).link(newDiv,{
									time: $.ui.transitionTime
								});
							}
						}).link(newDiv,{
							x:'0%',
							y:'0%'
						});
					}
					else{
						$(oldDiv).css3Animate({
							x:'0%',
							y:'0%',
							complete: function(){
								$(oldDiv).css3Animate({
									time: $.ui.transitionTime,
									complete : function(){
										Transitions.onTransitionEnd(oldDiv,newDiv);
									}
								}).link(newDiv,{
									y:'0%',
									time: $.ui.transitionTime
								});
							}
						}).link(newDiv,{
							x:'0%',
							y:'100%'
						});
					}
				},
				slideDownPanel : function(oldDiv,newDiv,back){
					if(!oldDiv || !newDiv || (oldDiv == newDiv))
						return;

					oldDiv.style.display = "block";
					newDiv.style.display = "block";
									
					$.ui.block();

					dynamicHeader.setFromPanel($(newDiv));

					if(back){
						$(oldDiv).css3Animate({
							x:'0%',
							y:'0%',
							complete: function(){
								$(oldDiv).css3Animate({
									y: '-100%',
									time: $.ui.transitionTime,
									complete : function(){
										Transitions.onTransitionEnd(oldDiv,newDiv);
									}
								}).link(newDiv,{
									time: $.ui.transitionTime
								});
							}
						}).link(newDiv,{
							x:'0%',
							y:'0%'
						});
					}
					else{
						$(oldDiv).css3Animate({
							x:'0%',
							y:'0%',
							complete: function(){
								$(oldDiv).css3Animate({
									time: $.ui.transitionTime,
									complete : function(){
										Transitions.onTransitionEnd(oldDiv,newDiv);
									}
								}).link(newDiv,{
									y:'0%',
									time: $.ui.transitionTime
								});
							}
						}).link(newDiv,{
							x:'0%',
							y:'-100%'
						});
					}
				}
			};
			/**
				Returned object
			*/
			return {
				state : '',
				transitionTime : '250ms',
				transitions : {
					'slide' : Transitions.slidePanel,
					'fade' : Transitions.fadePanel,
					'up' : Transitions.slideUpPanel,
					'down' : Transitions.slideDownPanel,
					'default' : Transitions.slidePanel
				},
				launch :function(){
					var self = this;
					_initialize.apply(this);
					this.state = 'completed';
					uiReadyCallback();
					if(this.splashScreenTimeout != 0){
						setTimeout(function(){
							self.hideSplashScreen();
						},self.splashScreenTimeout?self.splashScreenTimeout:2500);
					}
					else {
						this.hideSplashScreen();
					}
				}
				,
				refresh : function(){
					_calculateContentHeight();
				},
				showPanel : function(id,transition,back,ignoreHistory){
					if(id){
						transitionQueue.push('#'+shownPanel.id,id,transition,back,ignoreHistory);
						this.hideMenu();
					}
				},
				finishTransition: function(oldDiv, currDiv) {
					oldDiv.style.display = 'none';
					if (currDiv) this.clearAnimations(currDiv);
					if (oldDiv) this.clearAnimations(oldDiv);
				},
				clearAnimations: function(inViewDiv) {
					inViewDiv.style[$.feat.cssPrefix + 'Transform'] = "none";
					inViewDiv.style[$.feat.cssPrefix + 'Transition'] = "none";
				},
				block : function(opacity){
					opacity = (opacity && typeof opacity == 'number')?opacity:0;

					mask.css('opacity',opacity);
					mask.show();
				},
				unblock : function(){
					mask.hide();
				},
				showMenu : function(){
					if(menu.shown) return;
					menu.style.display = 'block';
					$(menu).css3Animate({
						x:0,
						y:0,
						complete: function(){
							mask.css3Animate({
								x: '200px',
								time: $.ui.transitionTime,
								complete: function(){
									mask.one('click',function(){
										//When the menu is shown, if the user clicks outside of the menu area, hide the menu
										$.ui.hideMenu();
									});

									menu.shown = true;

									$.ui.block();
								}
							}).link(content,{
								x: '200px',
								time: $.ui.transitionTime
							}).link(header,{
								x: '200px',
								time: $.ui.transitionTime
							});
						}
					});
					//content.style.webkitTransform = 'translate3d(200px,0,0)';
					//header.style.webkitTransform = 'translate3d(200px,0,0)';
					//mask.css('webkitTransform','translate3d(200px,0,0)');
				},
				hideMenu : function(){
					if(!menu.shown) return;
					$(menu).css3Animate({
						x:0,
						y:0,
						complete: function(){
							mask.css3Animate({
								x: '0',
								time: $.ui.transitionTime,
								complete: function(){
									menu.style.display = 'none';
									mask.one('click',function(){
										//When the menu is shown, if the user clicks outside of the menu area, hide the menu
										$.ui.hideMenu();
									});

									menu.shown = false;

									$.ui.unblock();
								}
							}).link(content,{
								x: '0',
								time: $.ui.transitionTime
							}).link(header,{
								x: '0',
								time: $.ui.transitionTime
							});
						}
					});
					// content.style.webkitTransform = 'translate3d(0,0,0)';
					// header.style.webkitTransform = 'translate3d(0,0,0)';
					// mask.css('webkitTransform','translate3d(0,0,0)');
				},
				toggleMenu : function(){
					if(menu.shown)
						this.hideMenu();
					else
						this.showMenu();
				},
				showSplashScreen : function(){
					if(splashScreen.shown) return;
					$(splashScreen).show();
				},
				hideSplashScreen : function(){
					if(!splashScreen.shown) return;
					$(splashScreen).hide();
				},
				toggleSplashScreen : function(){
					if(splashScreen.shown)
						this.showSplashScreen();
					else
						this.hideSplashScreen();
				},
				showBackButton : function(){
					if(backButton.shown) return;
					
					backButton.css('visibility','');
					backButton.shown = true;
				},
				hideBackButton : function(){
					if(backButton.shown || backButton.shown == undefined){;
						backButton.css('visibility','hidden');
						backButton.shown = false;
					}
				},
				toggleBackButton : function(){
					if(backButton.shown)
						this.hideBackButton();
					else 
						this.showBackButton();
				},
				ready : function(callback){
					if(this.state=='completed')
						callback();
					else
						uiReadyCallback = callback;
				},
				goBack : function(){
					if(history.isEmpty()) return;

					var historyEntry = history.pop();

					if(history.isEmpty()) this.hideBackButton();

					transitionQueue.push(historyEntry.toId,historyEntry.fromId,historyEntry.transition,true,true);

				},
				clearHistory : function(){
					history.clear();
				},
				openExternalLink : function(url){
					var ref = window.open(url, '_system');
				}
			};
		};


		var History = function(){
			var HistoryEntry = function(fromId,toId,transition){
				return {
					fromId : fromId,
					toId : toId,
					transition: transition
				}
			}

			var queue = [];

			return {
				push : function(fromId,toId,transition){
					queue.push(new HistoryEntry(fromId,toId,transition?transition:'default'));
				},
				pop : function(){
					return queue.pop();
				},
				clear : function(){
					queue = [];
				},
				isEmpty : function(){
					return queue.length==0
				}
			}
		}

		$.ui = new UI();

	})();

	$().ready(function(){
		if($.ui.autoLaunch == undefined || $.ui.autoLaunch){
			$.ui.launch();
		}
	});

})(jq);